/* 
 * Christian's SA3250HD & SA4250HD firewire command sender
 *
 * This program will accept a command line paramater to send a specific
 * command to a specific Scientific Atlanta set top box via firewire.
 * There is no verification of the device type and assumes that this was
 * predetermined say via a script such as sa3250_cmd_guid.
 *
 * USAGE: Usage: sa3250cmd [-v] -n <node> <command>
 *          -v : Verbose Mode
 *          -n : Node to change - required
 *   <command> : on, off, status
 *
 * RETURN Value: Dependent on command
 *               -1 = error
 *               0  = success but no return value
 *               on, off, and status return:
 *               1 = OFF
 *               2 = ON
 *
 * requires: libavc1394-dev libraw1394-dev
 *
 * compile with: gcc -o sa3250cmd sa3250cmd.c -lrom1394 -lavc1394 -lraw1394
 *
 * Copyright 2009 by Christian Szpilfogel <myfirstname@mylastname.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

 * 

*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <libavc1394/rom1394.h>
#include <libavc1394/avc1394.h>
#include <libraw1394/raw1394.h>

#define CMD_SUCCESS	0
#define CMD_STATUS	0
#define CMD_OFF		1
#define CMD_ON		2

#define CMDSTB_MASK	    0x00FFFFFF
#define CMDSTB_ERROR	-1
#define CMDSTB_STATUS   0x01FFb27F
#define CMDSTB_OFF      0x00FFb260
#define CMDSTB_ON       0x00FFb270

int verbose = 0;

quadlet_t *avc_cmd(raw1394handle_t handle, int node, quadlet_t cmd);

void usage()
{
   fprintf(stderr, "Usage: sa3250cmd [-v] -n <node> <command>\n");
   fprintf(stderr, "  -v : Verbose Mode\n");
   fprintf(stderr, "  -n : Node to change - required\n");
   fprintf(stderr, "  <command> : on, off, status\n");
}

int main(int argc, char **argv) 
{
    raw1394handle_t handle;
    int node = -1, port = 0;
    int command;
    int return_code;
    int c;
    int index;
    quadlet_t *avc_result;

    opterr = 0;
    while((c = getopt(argc, argv, "n:v")) != -1)
        {
        switch(c) 
            {
            case 'n':
                node = atoi(optarg);
                if (node < 0)
                    {
                    fprintf(stderr, "invalid node: %d\n", node);
                    exit(-1);
                    }
                break;
            case 'v':
                verbose = 1;
                break;
            default:
                fprintf(stderr, "bad command line argument: %c\n", optopt);
                usage();
                exit(-1);
            }
    	}

    if (node == -1)
        {
        fprintf(stderr, "-n NODE option required\n");
        usage();
        exit(-1);
    	}

    index = optind;
    if (index == argc)
        {
        fprintf(stderr, "Command Missing\n");
        usage();
        exit(-1);
    	}
    else if (argc > (index+1))
        {
        fprintf(stderr, "too many options\n");
        usage();
        exit(-1);
    	}

    if (!strcmp(argv[index], "on"))
        {
        command = CMDSTB_ON;
        } 
    else if (!strcmp(argv[index], "off"))
        {
        command = CMDSTB_OFF;
        }
    else if (!strcmp(argv[index], "status"))
        {
        command = CMDSTB_STATUS;
        }
    else
        {
        fprintf(stderr, "unknown command %s\n", argv[index]);
        usage();
        exit(-1);
        } 


    handle = raw1394_new_handle_on_port(port);
    if (!handle)
        {
        fprintf(stderr, "Failed to get raw1394 handle, Port: %d\n", port);
        exit(-1);
        }

	/* Issue the command */
    avc_result = avc_cmd(handle, node, command);
    if (avc_result[0] == CMDSTB_ERROR)
        {
        fprintf(stderr, "Set top box command failed\n");
        raw1394_destroy_handle(handle);
        exit(-1);
        }

	/* After Issuing the command, if it is on, off, or status then return the actual   */
	/* state after execution of the command and translate to a return code.            */
	/* The set top box can take time to stabalize after the last command so wait a bit */
    if ((command == CMDSTB_ON) ||
        (command == CMDSTB_OFF) ||
        (command == CMDSTB_STATUS) )
        {
        if (command != CMDSTB_STATUS)
            {                           /* Would be redundant for status command */
            sleep(1);
   	        avc_result = avc_cmd(handle, node, CMDSTB_STATUS);
            }
        switch (avc_result[0] & CMDSTB_MASK)
            {
            case CMDSTB_OFF:
            	return_code = CMD_OFF;
                break;
            case CMDSTB_ON:
               	return_code = CMD_ON;
                break;
            case CMDSTB_ERROR:
               	fprintf(stderr, "Set top box command failed\n");
               	return_code = -1;
               	break;
            default:
                fprintf(stderr, "Unexpected return result: 0x%08X\n", avc_result[0]);
               	return_code = -1;
            }
        }
    else
        {
        return_code = CMD_SUCCESS;
        }

    if (verbose)
        {
        printf("return code: %i\n", return_code);
   	    }

    raw1394_destroy_handle(handle);
    exit(return_code);

}


quadlet_t *avc_cmd(raw1394handle_t handle, int node, quadlet_t cmd)
{
    quadlet_t *response;

    if (verbose)
        {
        printf("  Sending Command: 0x%08X\n", cmd);
    	}

    response = avc1394_transaction_block(handle, node, &cmd, 1, 1);
    if (response)
        {
        if (AVC1394_MASK_RESPONSE(response[0]) == AVC1394_RESPONSE_ACCEPTED ||
            AVC1394_MASK_RESPONSE(response[0]) == AVC1394_RESPONSE_IMPLEMENTED)
            {
	        if (verbose)
                {
                printf("  Success, Response: 0x%08X\n", response[0]);
                }
            }
        else
            {
            fprintf(stderr, "  Command Failed, Response: 0x%08X\n", response[0]);
            response[0] = CMDSTB_ERROR;
            return(response);
            }
    	}
    else
        {
        fprintf(stderr, "  Command Failed, No Response\n");
        response[0] = CMDSTB_ERROR;
        return(response);
    	}

    return(response);
    
}

